import { stringify } from 'qs'
import dayjs from 'dayjs'
import isToday from 'dayjs/plugin/isToday'
import isYesterday from 'dayjs/plugin/isYesterday'
import weekday from 'dayjs/plugin/weekday'
import weekOfYear from 'dayjs/plugin/weekOfYear'

type ToastIcon = 'success' | 'loading' | 'error' | 'none' | 'fail' | 'exception'
class Tui {
  /**
   * Toast 提示
   * @param text
   * @param duration
   * @param icon
   */
  toast(text: string, duration: number = 2000, icon: ToastIcon = 'none'): void {
    uni.showToast({
      duration,
      title: text || '出错啦~',
      icon,
    })
  }

  /**
   * 加载提示框
   * @param text
   * @param mask
   */
  loading(text: string, mask = true) {
    uni.showLoading({ title: text, mask })
  }

  /**
   * 是否不为空
   * @param v
   * @returns
   */
  isNotBlank(v: string | number | boolean | null) {
    return (
      (v !== '' && v != null && v !== 'null' && v !== undefined && v !== 'undefined') ||
      String(v) === '0'
    )
  }

  /**
   * 设置缓存
   * @param key
   * @param value
   */
  set(key: string, value: any) {
    try {
      uni.setStorageSync(key, value)
    } catch (error) {
      console.log(`🚀 ~ Tui ~ set ~ error:`, error)
      this.toast('设置缓存失败!')
    }
  }

  /**
   * 获取缓存
   * @param key
   * @returns
   */
  get(key: string) {
    try {
      return uni.getStorageSync(key)
    } catch (error) {
      console.log(`🚀 ~ Tui ~ get ~ error:`, error)
      this.toast('获取缓存失败!')
      return null
    }
  }

  /**
   * 移除指定缓存
   * @param key
   */
  remove(key: string) {
    try {
      uni.removeStorageSync(key)
    } catch (error) {
      console.log(`🚀 ~ Tui ~ remove ~ error:`, error)
      this.toast('移除指定缓存失败!')
    }
  }

  /**
   * 清除缓存
   */
  clear() {
    try {
      uni.clearStorageSync()
    } catch (error) {
      console.log(`🚀 ~ Tui ~ clear ~ error:`, error)
      this.toast('清除缓存失败!')
    }
  }

  /**
   * 是否是安卓系统
   * @returns
   */
  isAndroid() {
    const res = uni.getSystemInfoSync()
    return res.platform.toLocaleLowerCase() === 'android'
  }

  /**
   * 是否是 iphoneX
   * @returns boolean
   */
  isPhoneX() {
    const res = uni.getSystemInfoSync()
    let flag = false
    const models = [
      'iphonex',
      'iphonexr',
      'iphonexsmax',
      'iphone11',
      'iphone11pro',
      'iphone11promax',
    ]
    const model = res.model.replace(/\s/g, '').toLowerCase()
    if (models.includes(model)) {
      flag = true
    }
    return flag
  }

  /**
   * 去除前后空格
   * @param value
   * @returns
   */
  trim(value: string) {
    return value.replace(/(^\s*)|(\s*$)/g, '')
  }

  /**
   * 去除所有空格
   * @param value
   * @returns
   */
  // eslint-disable-next-line lines-between-class-members
  trimAll(value: string) {
    return value.replace(/\s*/g, '')
  }

  /**
   * 替换相同字符串
   * @param str 字符串
   * @param searchValue 搜索值
   * @param replaceValue 替换值
   * @returns
   */
  replaceAll(str: string, searchValue: string, replaceValue: string) {
    return str.replaceAll(searchValue, replaceValue)
  }

  /**
   * 格式化手机号
   * @param phoneNumber
   * @returns
   */
  formatNumber(phoneNumber: string) {
    return phoneNumber.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
  }

  /**
   * 格式化金额
   * @param amount 金额
   * @param precision 精度
   * @returns
   */
  formatMoney(amount: string | number, precision = 2) {
    amount = String(amount)
    if (!amount || amount === '') return '0.00'
    return parseFloat(amount)
      .toFixed(precision)
      .replace(/\B(?=(\d{3})+(?!\d))/g, ',')
  }

  /**
   * 格式化日期
   * @param date 时间
   * @param separator 分隔符
   * @returns
   */
  formatDate(date: Date, separator = '-') {
    function padZero(num: number) {
      return num < 10 ? '0' + num : num
    }

    return (
      date.getFullYear() +
      separator +
      padZero(date.getMonth() + 1) +
      separator +
      padZero(date.getDate())
    )
  }

  /**
   * 获取日期时间段
   * @param type 1:今天 2:昨天 3:本周 4:本月 5:本年
   * @param time 时间 默认当前时间
   * @returns
   */
  getDateTimeSlot(type: 1 | 2 | 3 | 4 | 5, time: Date | string = new Date()) {
    dayjs.extend(isToday)
    dayjs.extend(isYesterday)
    dayjs.extend(weekday)
    dayjs.extend(weekOfYear)
    try {
      const now = dayjs(new Date(time))

      switch (type) {
        case 1: // 今天
          return {
            start: now.startOf('day').toDate(),
            end: now.endOf('day').toDate(),
          }

        case 2: // 昨天
          const yesterday = now.subtract(1, 'day')
          return {
            start: yesterday.startOf('day').toDate(),
            end: yesterday.endOf('day').toDate(),
          }

        case 3: // 本周
          return {
            start: now.startOf('week').toDate(),
            end: now.endOf('week').toDate(),
          }

        case 4: // 本月
          return {
            start: now.startOf('month').toDate(),
            end: now.endOf('month').toDate(),
          }

        case 5: // 本年
          return {
            start: now.startOf('year').toDate(),
            end: now.endOf('year').toDate(),
          }

        default:
          return null
      }
    } catch (error) {
      console.log(`🚀 ~ Tui ~ getDateTimeSlot ~ error:`, error)
      return null
    }
  }

  /**
   * 对象转URL参数
   * @param params
   * @returns
   */
  queryParams(params: Record<string, any>) {
    try {
      return '?' + stringify(params)
    } catch (error) {
      console.log(`🚀 ~ Tui ~ queryParams ~ error:参数转译错误`, error)
    }
  }

  /**
   * 生成唯一ID
   * @param key <string> uuid | uuid_no_dash | snowflake
   * @returns
   */
  generateId(key: 'uuid' | 'uuid_no_dash' | 'snowflake' = 'uuid'): string {
    if (key === 'uuid' || key === 'uuid_no_dash') {
      const template = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
      const uuid = template.replace(/[xy]/g, (c) => {
        const r = (Math.random() * 16) | 0
        const v = c === 'x' ? r : (r & 0x3) | 0x8
        return v.toString(16)
      })

      return key === 'uuid' ? uuid : uuid.replace(/-/g, '')
    } else if (key === 'snowflake') {
      const timestamp = BigInt(Date.now())
      const randomBits = BigInt(Math.floor(Math.random() * 4096))
      const snowflakeId = (timestamp << BigInt(12)) | randomBits
      return snowflakeId.toString()
    } else {
      throw new Error("Unsupported key. Use 'uuid', 'uuid_no_dash' or 'snowflake'.")
    }
  }

  /**
   * 节流
   * @param func <Function> 触发回调执行的函数
   * @param wait <Number> 时间间隔，单位ms
   * @param immediate  <Boolean> 在开始还是结束处触发，比如设置wait为1000ms，如果在一秒内进行了5次操作，只触发一次，如果immediate为true，那么就会在第一次操作 触发回调，如果为false，就会在第5次操作触发回调。
   * @returns
   */
  throttle(
    func: (...args: any[]) => void,
    wait: number = 500,
    immediate: boolean = true,
  ): (...args: any[]) => void {
    let timer: ReturnType<typeof setTimeout> | null = null
    let lastCallTime: number | null = null

    return (...args: any[]) => {
      const now = Date.now()

      if (immediate && lastCallTime === null) {
        func.apply(this, args)
        lastCallTime = now
      } else if (!timer) {
        const timeSinceLastCall = lastCallTime ? now - lastCallTime : 0

        if (timeSinceLastCall >= wait) {
          func.apply(this, args)
          lastCallTime = now
        } else {
          timer = setTimeout(() => {
            func.apply(this, args)
            lastCallTime = Date.now()
            timer = null
          }, wait - timeSinceLastCall)
        }
      }
    }
  }

  /**
   * 防抖
   * @param func <Function> 触发回调执行的函数
   * @param wait <Number> 时间间隔，单位ms
   * @param immediate <Number> 在开始还是结束处触发，如非特殊情况，一般默认为false即可
   * @returns
   */
  debounce(
    func: (...args: any[]) => void,
    wait: number = 500,
    immediate: boolean = false,
  ): (...args: any[]) => void {
    let timer: ReturnType<typeof setTimeout> | null = null

    return (...args: any[]) => {
      const callNow = immediate && !timer

      if (timer) {
        clearTimeout(timer)
      }

      timer = setTimeout(() => {
        timer = null
        if (!immediate) {
          func.apply(this, args)
        }
      }, wait)

      if (callNow) {
        func.apply(this, args)
      }
    }
  }
}

export default new Tui()
